﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Web;

namespace SIS_Wcf_Server.Model
{
    public class EfData : IData
    {
        
        private Model.SISDBEntities _data;

        public EfData()
        {
            _data = new Model.SISDBEntities();
        }

        public EfData(Model.SISDBEntities data)
        {
            _data = data;
        }


        public RestInformation CreateInformation(RestInformation information)
        {
            //Opret ny informations pakke. 
            var info = information.Information;            
            _data.Information.AddObject(info);

            //Opret datapakkerne.
            foreach (var data in information.InformationsData)
            {
                var infodata = data.InformationsData;
                _data.InformationsData.AddObject(infodata);
                //Tilføj relationen til informations objektet
                info.InformationsData.Add(infodata);
                //Opret evt. tags tilknyttede det enkelte data objekt.
                foreach (var tags in data.Tags)
                {
                    var tag = tags;
                    tag.Id = Guid.NewGuid();
                    tag = OpretTag(tag);

                    //Tilføj tag til data relationen
                    infodata.Tags.Add(tag);
                }
            }


            //Opret eller find de eksisternede lokationer.
            SaveAndRelateLocations(information.Lokationer);


            foreach (var lokation in information.Lokationer)
            {
                if (lokation.Type.ToUpper() == "GPS")
                {
                    var gps = SearchToGps(lokation);
                    var dbGps = GetExistingGps(gps);
                    info.gpsPositioner.Add(dbGps);
                   
                    continue;
                }


                var net = lokationToNet(lokation);
                var dbnet = GetExistingNet(net);

                info.net.Add(dbnet);

            }

            _data.SaveChanges();

            return information;

        }

        private Tags OpretTag(Tags tag)
        {
            //Check at tagget ikke er oprettet i forvejen.
            var tagexists = GetTag(tag.Tag);
            if (tagexists != null)
                return tagexists;

            _data.Tags.AddObject(tag);
            _data.SaveChanges();

            return tag;


        }

        private Tags GetTag(string tag)
        {
            return _data.Tags.Where(t => t.Tag == tag).FirstOrDefault();
        }

        public net OpretNetSoegning(net netData)
        {
                
                //Søg og se om posten findes i forvejen.
            var netExists = GetExistingNet(netData);
            if (netExists != null)
                return netExists;
                

            _data.net.AddObject(netData);
            _data.SaveChanges();
           
            return netData;
        }

        private net GetExistingNet(net netData)
        {
            return
                _data.net.Include("net1").Include("net2").Include("gpsPositioner").Include(
                    "Information.InformationsData.Tags").Where(n => n.Type == netData.Type & n.Value == netData.Value).
                    FirstOrDefault();
                                                
        }

        public gpsPositioner OpretGpsSoegning(gpsPositioner position)
        {
                //Check om positionen eksistere i forvejen og returner den hvis den gør

            var existingPosition = GetExistingGps(position);
            if (existingPosition != null)
                return existingPosition;
            
                _data.gpsPositioner.AddObject(position);
                _data.SaveChanges();

                return position;
        }


        private gpsPositioner SearchToGps(Lokation lokation)
        {
                                      
            string[] c = lokation.Value.Split(new char[] { '/' });

            var gps = new gpsPositioner
            {
                Lat = Convert.ToDouble( c[0] ),
                Long = Convert.ToDouble( c[1] ),
                ID = Guid.NewGuid(),
                DateTimeCreated = DateTime.Now
            };

            if (c.Length == 3)
                gps.Radius = Convert.ToInt32(c[2]);

            return gps;
        }

        private net lokationToNet(Lokation lokation)
        {
            var netData = new net
            {
                Id = Guid.NewGuid(),
                Type = lokation.Type,
                Value = lokation.Value,
                DateTimeCreated = DateTime.Now
            };

            return netData;
        }

        public void SaveAndRelateLocations(List<Lokation> locations)
        {

            var gpslocations = new ArrayList();
            var netlocations = new ArrayList();

            foreach (var location in locations)
            {
                if (location.Type.ToUpper() == "GPS")
                {


                    //GPS Værdi formatet skal være "lat/long"

                    var gps = SearchToGps(location);

                    gps = OpretGpsSoegning(gps);

                    gpslocations.Add(gps);

                    continue;
                }


                var netData = lokationToNet(location);

                netData = OpretNetSoegning(netData);
                netlocations.Add(netData);
            }

            //Link data 
            foreach (var netlocation in netlocations)
            {
                var netl = (net)netlocation;
                foreach (var netlocation1 in netlocations)
                {
                    if (netlocation1 != netlocation)
                        if (!netl.net1.Contains((net)netlocation1))
                            netl.net1.Add((net)netlocation1);
                }

                foreach (var gpslocation in gpslocations)
                {
                    if (!netl.gpsPositioner.Contains((gpsPositioner)gpslocation))
                        netl.gpsPositioner.Add((gpsPositioner)gpslocation);
                }
            }


            _data.SaveChanges();

        
        }

        public List<Information> GetInformationForLocations(List<Lokation> locations)
        {

            //Løb søge lokationerne igennem og lav en liste over gps + net lokationer.
            var gpspositioner = new List<gpsPositioner>();
            var netpositioner = new List<net>();
            var information = new List<Information>();

            foreach (var location in locations)
            {
                if (location.Type.ToUpper() == "GPS")
                {
                    var g = GetExistingGpsIncludingRadius(SearchToGps(location));
                    if ( g != null )
                        gpspositioner.Add( g );
                    continue;
                }

                netpositioner.Add(GetExistingNet(lokationToNet(location)));
            }

            foreach (var positioner in gpspositioner)
            {
                foreach (var i in positioner.Information)
                {
                    if (!information.Contains(i))
                    {
                        information.Add(i);
                    }
                }
            }

            foreach (var positioner in netpositioner)
            {
                foreach (var i in positioner.Information)
                {
                    if (!information.Contains(i))
                    {
                        information.Add(i);
                    }
                }
            }


            return information;
        }

        private gpsPositioner GetExistingGps(gpsPositioner position)
        {
            return (from g in _data.gpsPositioner.Include("net").Include("Information.InformationsData.Tags")
                    where g.Lat == position.Lat & g.Long == position.Long
                    select g).FirstOrDefault();
        }

        private gpsPositioner GetExistingGpsIncludingRadius(gpsPositioner position)
        {
            return (from g in _data.gpsPositioner.Include("net").Include("Information.InformationsData.Tags")
                    where g.Lat == position.Lat & g.Long == position.Long
                    select g).FirstOrDefault();

            //var afstand = (from g in _data.gpsPositioner
            //               select new {afstand = _data.CalcDistance(position.Lat, position.Long, g.Lat, g.Long).FirstOrDefault(), g.Radius}).FirstOrDefault();

            
            //return (from g in _data.gpsPositioner.Include("net").Include("Information.InformationsData.Tags")
            //        let dist = _data.CalcDistance(position.Lat,position.Long,g.Lat,g.Long).FirstOrDefault()
            //        where dist < g.Radius
            //        select g).FirstOrDefault();
        }

 
        public List<Lokation> GetAllLocations()
        {
            // Hvad er forskellen på de to statemtnes nedenfor
            var gps = (from g in _data.gpsPositioner select g);
            var net = _data.net.Select(n => n);

            var res = new List<Lokation>();
            foreach (var g in gps)
                res.Add(new Lokation() { Type = "GPS", Value = g.Lat + "/" + g.Long });

            foreach (var n in net)
                res.Add(new Lokation() { Type = n.Type, Value = n.Value });

            return res;
        }




        public List<net> GetLatestNets()
        {

            // TODO: Det her kan helt sikkert skrives om
            var x = (from d in _data.none_geocoded_nets select d.netid);

            List<net> r = new List<net>();

            foreach (var v in _data.net.Include("gpsPositioner"))
            {
                if (x.ToList().Contains(v.Id))
                {
                    r.Add(v);
                }
            }

            return r;
        }

        public void store()
        {
            _data.SaveChanges();
        }

        
        public List<net> GetNetForLocationByType(Lokation location, string type)
        {
            List<net> result = new List<net>();
            net source = this.lokationToNet(location);
            foreach (net n in source.net2)
            {
                if (n.Type == type)
                {
                    result.Add( n );
                }
            }
            foreach (net n in source.net1)
            {
                if (n.Type == type)
                {
                    result.Add(n);
                }
            }
            return result;
        }


        public void tagLokationAsNoneGeoCoded(Lokation l)
        {
            
            var netData = lokationToNet(l);

            var netExists = GetExistingNet(netData);

            if (netExists == null)
            {
                return;
            }

            var thenew = new none_geocoded_nets();
            thenew.id = Guid.NewGuid();
            thenew.netid = netExists.Id;

            _data.none_geocoded_nets.AddObject( thenew );

            this.store();

        }

        #region IData Members


        public net getNetFromLokation(Lokation l)
        {
        
            return
                _data.net.Include("net1").Include("net2").Include( "geocoded_nets" ).Include(
                    "Information.InformationsData.Tags").Where(n => n.Type == l.Type & n.Value == l.Value).
                    FirstOrDefault();
                                                
        }

        #endregion
    }
}